﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Xml.Linq;
using Timer = System.Threading.Timer;

namespace OvenParameterForm.Util
{
    class PLC
    {
        #region //标准库
        //[DllImport("StandardModbusApi.dll", EntryPoint = "Init_ETH_String", CallingConvention = CallingConvention.Cdecl)]
        //public static extern bool Init_ETH_String(string sIpAddr, int nNetId = 0, int IpPort = 502);

        //[DllImport("StandardModbusApi.dll", EntryPoint = "Exit_ETH", CallingConvention = CallingConvention.Cdecl)]
        //public static extern bool Exit_ETH(int nNetId = 0);

        //[DllImport("StandardModbusApi.dll", EntryPoint = "H3u_Write_Soft_Elem", CallingConvention = CallingConvention.Cdecl)]
        //public static extern int H3u_Write_Soft_Elem(SoftElemType eType, int nStartAddr, int nCount, byte[] pValue, int nNetId = 0);

        //[DllImport("StandardModbusApi.dll", EntryPoint = "H3u_Read_Soft_Elem", CallingConvention = CallingConvention.Cdecl)]
        //public static extern int H3u_Read_Soft_Elem(SoftElemType eType, int nStartAddr, int nCount, byte[] pValue, int nNetId = 0);

        //[DllImport("StandardModbusApi.dll", EntryPoint = "H3u_Read_Soft_Elem_Float", CallingConvention = CallingConvention.Cdecl)]
        //public static extern int H3u_Read_Soft_Elem_Float(SoftElemType eType, int nStartAddr, int nCount, float[] pValue, int nNetId = 0);

        #region //标准库
        [DllImport("StandardModbusApi.dll", EntryPoint = "Init_ETH_String", CallingConvention = CallingConvention.Cdecl)]
        public static extern bool Init_ETH_String(string sIpAddr, int nNetId = 0, int IpPort = 502);

        [DllImport("StandardModbusApi.dll", EntryPoint = "Exit_ETH", CallingConvention = CallingConvention.Cdecl)]
        public static extern bool Exit_ETH(int nNetId = 0);

        //[DllImport("StandardModbusApi.dll", EntryPoint = "H3u_Write_Soft_Elem", CallingConvention = CallingConvention.Cdecl)]
        //public static extern int H3u_Write_Soft_Elem(SoftElemType eType, int nStartAddr, int nCount, byte[] pValue, int nNetId = 0);

        //[DllImport("StandardModbusApi.dll", EntryPoint = "H3u_Read_Soft_Elem", CallingConvention = CallingConvention.Cdecl)]
        //public static extern int H3u_Read_Soft_Elem(SoftElemType eType, int nStartAddr, int nCount, byte[] pValue, int nNetId = 0);

        //[DllImport("StandardModbusApi.dll", EntryPoint = "H3u_Read_Soft_Elem_Float", CallingConvention = CallingConvention.Cdecl)]
        //public static extern int H3u_Read_Soft_Elem_Float(SoftElemType eType, int nStartAddr, int nCount, float[] pValue, int nNetId = 0);

        //[DllImport("StandardModbusApi.dll", EntryPoint = "H5u_Write_Soft_Elem", CallingConvention = CallingConvention.Cdecl)]
        //public static extern int H5u_Write_Soft_Elem(SoftElemType eType, int nStartAddr, int nCount, byte[] pValue, int nNetId = 0);

        //[DllImport("StandardModbusApi.dll", EntryPoint = "H5u_Read_Soft_Elem", CallingConvention = CallingConvention.Cdecl)]
        //public static extern int H5u_Read_Soft_Elem(SoftElemType eType, int nStartAddr, int nCount, byte[] pValue, int nNetId = 0);

        //[DllImport("StandardModbusApi.dll", EntryPoint = "H5u_Read_Soft_Elem_Float", CallingConvention = CallingConvention.Cdecl)]
        //public static extern int H5u_Read_Soft_Elem_Float(SoftElemType eType, int nStartAddr, int nCount, float[] pValue, int nNetId = 0);


        [DllImport("StandardModbusApi.dll", EntryPoint = "Am600_Write_Soft_Elem", CallingConvention = CallingConvention.Cdecl)]
        public static extern int Am600_Write_Soft_Elem(SoftElemType eType, int nStartAddr, int nCount, byte[] pValue, int nNetId = 0);

        [DllImport("StandardModbusApi.dll", EntryPoint = "Am600_Read_Soft_Elem", CallingConvention = CallingConvention.Cdecl)]
        public static extern int Am600_Read_Soft_Elem(SoftElemType eType, int nStartAddr, int nCount, byte[] pValue, int nNetId = 0);

        #endregion





        /******************************************************************************
         1.功能描述 : 写Am600软元件
         2.返 回 值 :1 成功  0 失败
         3.参    数 : nNetId:网络链接编号
                      eType：软元件类型    ELEM_QX = 0//QX元件  ELEM_MW = 1 //MW元件
                      nStartAddr:软元件起始地址（QX元件由于带小数点，地址需要乘以10去掉小数点，如QX10.1，请输入101，MW元件直接就是它的元件地址不用处理）
                      nCount：软元件个数
                      pValue：数据缓存区
        4.注意事项 :  1.x和y元件地址需为8进制; 
                      2. 当元件位C元件双字寄存器时，每个寄存器需占4个字节的数据
                      3.如果是写位元件，每个位元件的值存储在一个字节中
        ******************************************************************************/
        //[DllImport("StandardModbusApi.dll", EntryPoint = "Am600_Write_Soft_Elem", CallingConvention = CallingConvention.Cdecl)]
        //public static extern int Am600_Write_Soft_Elem(SoftElemType eType, int nStartAddr, int nCount, byte[] pValue, int nNetId = 0);
        [DllImport("StandardModbusApi.dll", EntryPoint = "Am600_Write_Soft_Elem_Int16", CallingConvention = CallingConvention.Cdecl)]
        public static extern int Am600_Write_Soft_Elem_Int16(SoftElemType eType, int nStartAddr, int nCount, short[] pValue, int nNetId = 0);
        [DllImport("StandardModbusApi.dll", EntryPoint = "Am600_Write_Soft_Elem_Int32", CallingConvention = CallingConvention.Cdecl)]
        public static extern int Am600_Write_Soft_Elem_Int32(SoftElemType eType, int nStartAddr, int nCount, int[] pValue, int nNetId = 0);
        [DllImport("StandardModbusApi.dll", EntryPoint = "Am600_Write_Soft_Elem_UInt16", CallingConvention = CallingConvention.Cdecl)]
        public static extern int Am600_Write_Soft_Elem_UInt16(SoftElemType eType, int nStartAddr, int nCount, ushort[] pValue, int nNetId = 0);
        [DllImport("StandardModbusApi.dll", EntryPoint = "Am600_Write_Soft_Elem_UInt32", CallingConvention = CallingConvention.Cdecl)]
        public static extern int Am600_Write_Soft_Elem_UInt32(SoftElemType eType, int nStartAddr, int nCount, uint[] pValue, int nNetId = 0);
        [DllImport("StandardModbusApi.dll", EntryPoint = "Am600_Write_Soft_Elem_Float", CallingConvention = CallingConvention.Cdecl)]
        public static extern int Am600_Write_Soft_Elem_Float(SoftElemType eType, int nStartAddr, int nCount, float[] pValue, int nNetId = 0);



        /******************************************************************************
         1.功能描述 : 读Am600软元件
         2.返 回 值 :1 成功  0 失败
         3.参    数 : nNetId:网络链接编号
                      eType：软元件类型   ELEM_QX = 0//QX元件  ELEM_MW = 1 //MW元件
                      nStartAddr:软元件起始地址（QX元件由于带小数点，地址需要乘以10去掉小数点，如QX10.1，请输入101，其它元件不用处理）
                      nCount：软元件个数
                      pValue：返回数据缓存区
         4.注意事项 : 如果是读位元件，每个位元件的值存储在一个字节中，pValue数据缓存区字节数必须是8的整数倍
        ******************************************************************************/
        //[DllImport("StandardModbusApi.dll", EntryPoint = "Am600_Read_Soft_Elem", CallingConvention = CallingConvention.Cdecl)]
        //public static extern int Am600_Read_Soft_Elem(SoftElemType eType, int nStartAddr, int nCount, byte[] pValue, int nNetId = 0);
        [DllImport("StandardModbusApi.dll", EntryPoint = "Am600_Read_Soft_Elem_Int16", CallingConvention = CallingConvention.Cdecl)]
        public static extern int Am600_Read_Soft_Elem_Int16(SoftElemType eType, int nStartAddr, int nCount, short[] pValue, int nNetId = 0);
        [DllImport("StandardModbusApi.dll", EntryPoint = "Am600_Read_Soft_Elem_Int32", CallingConvention = CallingConvention.Cdecl)]
        public static extern int Am600_Read_Soft_Elem_Int32(SoftElemType eType, int nStartAddr, int nCount, int[] pValue, int nNetId = 0);
        [DllImport("StandardModbusApi.dll", EntryPoint = "Am600_Read_Soft_Elem_UInt16", CallingConvention = CallingConvention.Cdecl)]
        public static extern int Am600_Read_Soft_Elem_UInt16(SoftElemType eType, int nStartAddr, int nCount, ushort[] pValue, int nNetId = 0);
        [DllImport("StandardModbusApi.dll", EntryPoint = "Am600_Read_Soft_Elem_UInt32", CallingConvention = CallingConvention.Cdecl)]
        public static extern int Am600_Read_Soft_Elem_UInt32(SoftElemType eType, int nStartAddr, int nCount, uint[] pValue, int nNetId = 0);
        [DllImport("StandardModbusApi.dll", EntryPoint = "Am600_Read_Soft_Elem_Float", CallingConvention = CallingConvention.Cdecl)]
        public static extern int Am600_Read_Soft_Elem_Float(SoftElemType eType, int nStartAddr, int nCount, float[] pValue, int nNetId = 0);


        #endregion

        public static ConcurrentDictionary<string, string> PlCdataConcurrentDictionary =
            new ConcurrentDictionary<string, string>();

        public static event EventHandler<string> ConnectFailedEvent;
        public static System.Threading.Timer updateTimer = new Timer(PLCDataUpdate, null, Timeout.Infinite, 1000);

        private static void PLCDataUpdate(object state)
        {
            foreach (KeyValuePair<string, string> kvp in PlCdataConcurrentDictionary)
            {
                PlCdataConcurrentDictionary[kvp.Key] = InovanceAM400PLCRead(kvp.Key);
            }
        }

       public static string InovanceAM400PLCRead(string kvpKey, int length = 32)
        {
            if (string.IsNullOrEmpty(kvpKey))
            {
                return "-1";
            }
            string str = kvpKey.Trim().Substring(0, 2);

            if (str.Equals("MW"))
            {
                int nStartAddr = int.Parse(kvpKey.Trim().Substring(2, kvpKey.Length - 2));
                int nCount = 2;
                int[] pValue = new int[2];//缓冲区
                short[] psValue = new short[2];
                int nNetId = 0;
                try
                {
#if NoPLC
                    return "0";
#endif
                    //调用api读数据
                    int nRet = length.Equals(32) ? Am600_Read_Soft_Elem_Int32(SoftElemType.ELEM_MW, nStartAddr, nCount, pValue, nNetId) :
                    Am600_Read_Soft_Elem_Int16(SoftElemType.ELEM_MW, nStartAddr, nCount, psValue, nNetId);
                    if (1 != nRet)
                    {
                        ConnectFailedEvent?.Invoke(null, kvpKey);
                        // GlobalVariable.myMessage.Message("读寄存器MW" + nStartAddr + "失败");
                        // GlobalVariable.myMessage.Show();
                        //  GlobalVariable.myLog.writeLog("错误地址是" + kvpKey + "," + nStartAddr);
                    }
                    return length.Equals(32) ? pValue[0].ToString() : psValue[0].ToString();


                }
                catch (Exception e)
                {
                    MessageBox.Show(e.Message);
                }
            }
            else if (str.Equals("QX"))
            {
                int nStartAddr = int.Parse(kvpKey.Trim().Substring(2, kvpKey.Length - 2));
                int nCount = 1;
                byte[] pValue = new byte[1];//缓冲区
                int nNetId = 0;
                try
                {
#if NoPLC
                    return "0";
#endif
                    //调用api写数据
                    int nRet = Am600_Read_Soft_Elem(SoftElemType.ELEM_QX, nStartAddr, nCount, pValue, nNetId);
                    if (1 != nRet)
                    {
                        // GlobalVariable.myMessage.Message("读寄存器QX" + nStartAddr + "失败");
                        //     GlobalVariable.myMessage.Show();
                        //  GlobalVariable.myLog.writeLog("错误地址是" + kvpKey + "," + nStartAddr);
                        //MessageBox.Show(("读寄存器QX" + nStartAddr + "失败"));
                    }
                    return pValue[0].ToString();

                }

                catch (Exception e)
                {
                    MessageBox.Show(e.Message);//try{}catch{}在try里面发生的错误不执行try里面的内容，直接跳到catch /e:异常参数
                }
            }
            return "-1";
        }

        public static bool open()
        {
            int nNetId = 0;
            int nIpPort = 502;
            XDocument PLCXML = new XDocument(new XElement("PLCaddress"));
            PLCXML.Save("PLCXML.xml");
            try
            {
                bool result = Init_ETH_String("192.168.1.88", nNetId, nIpPort);
                result = result ? true : Init_ETH_String("192.168.1.88", nNetId, nIpPort);
#if NoPLC
                return true;
#endif
                if (result)
                {
                    updateTimer.Change(0, 500);
                }

                return result;

            }
            catch (Exception e)
            {
                MessageBox.Show(e.Message);
                return false;
            }
        }
        public static void close()
        {
            int nNetId = 0;
            try
            {
                bool result = Exit_ETH(nNetId);
                if (result == true)
                    MessageBox.Show("连接关闭");
                else
                    MessageBox.Show("关闭连接失败");
            }
            catch (Exception e)
            {
                MessageBox.Show(e.Message);
            }
            return;
        }

        /// <summary>
        /// 写PLC
        /// </summary>
        /// <param name="name">PLC寄存器地址</param>
        /// <param name="date">要写入的值</param>

        public static void WriteData(string name, String date)
        {
            StackTrace st = new StackTrace();
            StackFrame sf =st.GetFrame(1);
            _MethodBase calledMethod=sf.GetMethod();
            string str = name.Trim().Substring(0, 2);//Trim()去掉开头和末尾空格和其它字符.Substring(0, 2)从头开始截取2个字符

            if (str.Equals("MW"))//如果截取的头部字符串是MW
            {
                int nStartAddr = int.Parse(name.Trim().Substring(2, name.Length - 2));//获取地址的数字部分
                int nCount = 2;
                int[] pValue = new int[2];//缓冲区，2个整型
                int nNetId = 0;

                int nValue = int.Parse(date);//将数据转为整数

                pValue[0] = nValue;//赋值给缓存组1
                pValue[1] = nValue;//赋值给缓存组2

                try
                {
                    // 调用api写数据，软元素类型为MW，地址，长度，缓存数组，ID
                    int nRet = Am600_Write_Soft_Elem_Int32(SoftElemType.ELEM_MW, nStartAddr, nCount, pValue, nNetId);
                    if (1 != nRet)
                    {
                        int nRet1 = Am600_Write_Soft_Elem_Int32(SoftElemType.ELEM_MW, nStartAddr, nCount, pValue, nNetId);
                        if (1 != nRet1)
                        {
                            int nRet2 = Am600_Write_Soft_Elem_Int32(SoftElemType.ELEM_MW, nStartAddr, nCount, pValue, nNetId);
                            if (1 != nRet2)
                            {
                              

                            }

                        }
                    }
                    else
                    {

                        // ReadRecordingToXMLAsync(name, pValue[0].ToString());

                    }
                }
                catch (Exception e)
                {
                    MessageBox.Show(e.Message);
                }
            }
            else if (str.Equals("QX"))
            {
                int nStartAddr = int.Parse(name.Trim().Substring(2, name.Length - 2));
                int nCount = 1;
                byte[] pValue = new byte[1];//缓冲区，长度为1个字节
                int nNetId = 0;

                pValue[0] = byte.Parse(date);
                try
                {
                    // 调用api写数据，软元素类型为QX，地址，长度，缓存数组，ID
                    int nRet = Am600_Write_Soft_Elem(SoftElemType.ELEM_QX, nStartAddr, nCount, pValue, nNetId);
                    if (1 != nRet)
                    {
                        int nRet1 = Am600_Write_Soft_Elem(SoftElemType.ELEM_QX, nStartAddr, nCount, pValue, nNetId);
                        if (1 != nRet1)
                        {
                            int nRet2 = Am600_Write_Soft_Elem(SoftElemType.ELEM_QX, nStartAddr, nCount, pValue, nNetId);
                            if (1 != nRet2)
                            {
                                

                            }

                        }

                        //MessageBox.Show((""写寄存器QX"+nStartAddr+"失败""));
                    }
                    else
                    {
                        // ReadRecordingToXMLAsync(name, pValue[0].ToString());

                    }
                }
                catch (Exception e)
                {
                    MessageBox.Show(e.Message);
                }
            }
        }

        public static string ReadData(string adr, int length = 32)
        {
            return PlCdataConcurrentDictionary.GetOrAdd(adr, k => InovanceAM400PLCRead(adr, length));
        }
    }
    public enum SoftElemType
    {
        ELEM_QX = 0,     //QX元件
        ELEM_MW = 1,     //MW元件
    }
}
